package com.time.admin.domain.utils;

import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.ext.toc.TocExtension;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.ast.TextCollectingVisitor;
import com.vladsch.flexmark.util.data.MutableDataSet;

import java.util.Arrays;

/**
 * Markdown 工具类：用于将 Markdown 内容剥离标记，统计纯文字、单词数，及符合自定义规则的“文章字数”。
 * <p>
 * “文章字数”定义为：剥离所有 Markdown 标记后，统计其中 **中文字符 + 英文字母／数字 + 标点符号** 的总和。
 * <p>
 * 使用了 flexmark-java（Parser + TextCollectingVisitor）来解析 Markdown 并提取纯文本。
 * 支持 Unicode 辅助平面字符（code point > U+FFFF）遍历。
 *
 * @author HuangQi
 * @date Created in 11:43 2025/11/14
 */
public class MarkdownStatisticsUtil {

    /**
     * 用于 Markdown 解析的 Parser 实例。
     */
    private static final Parser PARSER;

    static {
        MutableDataSet options = new MutableDataSet();
        // 启用 TOC 和表格扩展（如需更多可在此扩展）
        options.set(Parser.EXTENSIONS,
                Arrays.asList(TocExtension.create(), TablesExtension.create()));
        PARSER = Parser.builder(options).build();
    }

    /**
     * 将 Markdown 内容剥离标记后，统计纯文本的 **字符数**（包括所有字符，空格、换行、标点、中文、英文等）。
     *
     * @param markdown 原始 Markdown 文本
     * @return 剥离标记后纯文本的字符总数；如输入为空或 null 返回 0
     */
    public static int countPlainTextChars(String markdown) {
        if (markdown == null || markdown.isEmpty()) {
            return 0;
        }
        Node document = PARSER.parse(markdown);
        TextCollectingVisitor visitor = new TextCollectingVisitor();
        String plain = visitor.collectAndGetText(document);
        return plain.length();
    }

    /**
     * 将 Markdown 内容剥离标记后，按空白（空格、换行、制表符等）分词，统计 **单词数**。
     *
     * @param markdown 原始 Markdown 文本
     * @return 分词后的单词数；如输入为空或 null 返回 0
     */
    public static int countWords(String markdown) {
        if (markdown == null || markdown.isEmpty()) {
            return 0;
        }
        Node document = PARSER.parse(markdown);
        TextCollectingVisitor visitor = new TextCollectingVisitor();
        String plain = visitor.collectAndGetText(document);

        String trimmed = plain.trim();
        if (trimmed.isEmpty()) {
            return 0;
        }
        String[] words = trimmed.split("\\s+");
        return words.length;
    }

    /**
     * 将 Markdown 内容剥离标记后，统计 **“中文字符 + 英文字母/数字 + 标点符号”** 总数，作为文章字数。
     * <p>
     * 遍历纯文本中的每一个 Unicode code point，分别判断：
     * - 是否中文汉字（包括 CJK 通用区 + 扩展区）；
     * - 是否英文字母或数字；
     * - 是否标点符号（使用 Character.getType 判断）。
     *
     * @param markdown 原始 Markdown 文本
     * @return 符合上述规则的字符总数；如输入为空或 null 返回 0
     */
    public static Long countArticleLength(String markdown) {
        if (markdown == null || markdown.isEmpty()) {
            return 0L;
        }
        Node document = PARSER.parse(markdown);
        TextCollectingVisitor visitor = new TextCollectingVisitor();
        String plain = visitor.collectAndGetText(document);

        long count = 0L;
        int length = plain.length();
        for (int offset = 0; offset < length; ) {
            int codePoint = plain.codePointAt(offset);
            // 判断并统计
            if (isChinese(codePoint)
                    || Character.isLetterOrDigit(codePoint)
                    || isPunctuation(codePoint)) {
                count++;
            }
            // 跳过这个 code point 所占的 char 数（1 或 2）
            offset += Character.charCount(codePoint);
        }
        return count;
    }

    /**
     * 判断一个 Unicode code point 是否为 “中文汉字”。
     * <p>
     * 此处使用 Character.UnicodeBlock 判断汉字所属区块：包括
     * - CJK_UNIFIED_IDEOGRAPHS
     * - CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
     * - CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
     * <p>
     * 如需覆盖更多扩展区（C、D、E、F 等）可进一步添加。
     *
     * @param codePoint Unicode code point
     * @return true 如果属于上述汉字区块；否则 false
     */
    private static boolean isChinese(int codePoint) {
        Character.UnicodeBlock block = Character.UnicodeBlock.of(codePoint);
        return block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B;
        // 如需要，可继续添加更多区块判断
    }

    /**
     * 判断一个 Unicode code point 是否为 “标点符号”。
     * <p>
     * 此处通过 Character.getType(codePoint) 判断：
     * - CONNECTOR_PUNCTUATION
     * - DASH_PUNCTUATION
     * - START_PUNCTUATION
     * - END_PUNCTUATION
     * - INITIAL_QUOTE_PUNCTUATION
     * - FINAL_QUOTE_PUNCTUATION
     * - OTHER_PUNCTUATION
     * <p>
     * 如果你还有其它“标点”定义（如数学符号、全角空格等），可进一步扩展逻辑。
     *
     * @param codePoint Unicode code point
     * @return true 如果属于标点类型；否则 false
     */
    private static boolean isPunctuation(int codePoint) {
        int type = Character.getType(codePoint);
        return type == Character.CONNECTOR_PUNCTUATION
                || type == Character.DASH_PUNCTUATION
                || type == Character.START_PUNCTUATION
                || type == Character.END_PUNCTUATION
                || type == Character.INITIAL_QUOTE_PUNCTUATION
                || type == Character.FINAL_QUOTE_PUNCTUATION
                || type == Character.OTHER_PUNCTUATION;
    }

    /**
     * 示例 main 方法（测试用）。
     * 可用于快速验证效果：剥离 Markdown → 输出三项统计结果。
     */
    public static void main(String[] args) {
        String sample = "# 标题\n\n这是 **粗体** 文本，带有 [链接](https://example.com)。\n\n- 列表项1\n- 列表项2\n";
        System.out.println("字符数: " + countPlainTextChars(sample));
        System.out.println("单词数: " + countWords(sample));
        System.out.println("文章字数(中+英+标点): " + countArticleLength(sample));
    }
}

